package de.tudarmstadt.ukp.muip.fibqdojo.io.raw;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.StartElement;
import javax.xml.transform.TransformerException;

import com.google.common.base.Optional;

import de.tudarmstadt.ukp.muip.fibqdojo.interfaces.IFIBQSentence;
import de.tudarmstadt.ukp.muip.fibqdojo.interfaces.IFIBQToken;
import de.tudarmstadt.ukp.muip.fibqdojo.io.Writer;
import de.tudarmstadt.ukp.muip.fibqdojo.util.FIBQUtil;
import de.tudarmstadt.ukp.muip.fibqdojo.visitor.DetermineTokenKindVisitor;
import de.tudarmstadt.ukp.muip.fibqdojo.visitor.GetTokenListVisitor;

/**
 * Writes sentences in the IFIBQSentence format to an external file in order to
 * be able to store input data which went through preprocessing and reuse them
 * for various FIBQ-creation tasks and hence, avoid preproccesing of the same
 * data over and over again.
 * 
 * @author Tobias Horsmann
 */
public class RawSentenceWriter extends Writer {

    /**
     * Convenience constructor. Uses a whitespace as separator and UTF-8 as file
     * encoding
     * 
     * @throws XMLStreamException
     * @throws FileNotFoundException
     */
    public RawSentenceWriter(String path) throws ParserConfigurationException, FileNotFoundException,
            XMLStreamException {
        super(path, "UTF-8", " ");
    }

    public RawSentenceWriter(String path, String encoding, String tokenSeparator) throws ParserConfigurationException,
            FileNotFoundException, XMLStreamException {
        super(path, encoding, tokenSeparator);
    }

    public void write(List<IFIBQSentence> sentences) throws ParserConfigurationException, TransformerException,
            IOException, XMLStreamException {
        for (IFIBQSentence sentence : sentences) {
            writeSentence(sentence);
        }
    }

    private void writeSentence(IFIBQSentence sentence) throws XMLStreamException {
        int kind = determineKind(sentence);
        openSentenceTag(sentence, kind);
        for (IFIBQToken token : FIBQUtil.getToken(sentence)) {
            writeToken(kind, token);
        }
        closeSentenceTag(sentence);
    }

    private void closeSentenceTag(IFIBQSentence sentence) throws XMLStreamException {
        eventWriter.add(eventFactory.createEndElement("", "", SENTENCE));
    }

    private void openSentenceTag(IFIBQSentence sentence, int kind) throws XMLStreamException {
        StartElement sentStartElement = eventFactory.createStartElement("", "", SENTENCE);
        Attribute kindAttribute = eventFactory.createAttribute("kind", kind + "");
        eventWriter.add(sentStartElement);
        eventWriter.add(kindAttribute);
    }

    private void writeToken(int kind, IFIBQToken token) throws XMLStreamException {
        switch (kind) {
        case 1:
            processToken(token);
            return;
        case 2:
            processJapaneseToken(token);
            return;
        }

        throw new IllegalArgumentException("Sentence contained unknown kind of token with kind id: " + kind);
    }

    private void processJapaneseToken(IFIBQToken token) throws XMLStreamException {
        openTokenTag();
        new JapaneseTokenWriter(eventFactory, eventWriter).processToken(token);
        closeTokenTag();
    }

    private void processToken(IFIBQToken token) throws XMLStreamException {
        openTokenTag();
        new TokenWriter(eventFactory, eventWriter).processToken(token);
        closeTokenTag();
    }

    private void closeTokenTag() throws XMLStreamException {
        eventWriter.add(eventFactory.createEndElement("", "", TOKEN));
    }

    private void openTokenTag() throws XMLStreamException {
        StartElement sentStartElement = eventFactory.createStartElement("", "", TOKEN);
        eventWriter.add(sentStartElement);
    }

    private int determineKind(IFIBQSentence sentence) {
        GetTokenListVisitor tokenList = new GetTokenListVisitor();
        sentence.accept(tokenList);
        List<IFIBQToken> tokens = tokenList.getTokens();
        if (tokens.size() > 0) {
            return determineKind(tokenList.getTokens().get(0));
        }
        return -1;
    }

    private int determineKind(IFIBQToken token) {
        DetermineTokenKindVisitor kindVisitor = new DetermineTokenKindVisitor();
        token.accept(kindVisitor);
        Optional<Integer> kind = kindVisitor.getKind();
        if (kind.isPresent()) {
            return kind.get();
        }
        return -1;
    }

}
